package net.qiqbframework.persisthanding.dao;

import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.common.utils.CollectionUtil;
import org.javers.core.Changes;
import org.javers.core.Javers;
import org.javers.core.diff.Change;
import org.javers.core.diff.changetype.ValueChange;
import org.javers.core.diff.changetype.container.*;
import org.javers.core.metamodel.object.InstanceId;

import java.util.*;

@Slf4j
public class BatchBasicEntityPoDiff {


    public static <Po extends BasicEntityPo> void dynamicUpdate(Collection<Po> newCollection, Collection<Po> oldCollection, BasicEntityPoDao<Po> basicEntityPoDao) {
        if (CollectionUtil.isEmpty(newCollection) && CollectionUtil.isEmpty(oldCollection)) {
            log.info("动态更新两个集合都是空");
            return;
        }
        Class<Po> type = null;
        if (CollectionUtil.isNotEmpty(newCollection)) {
            final Optional<Po> first = newCollection.stream().findFirst();
            if (first.isPresent()) {
                type = (Class<Po>) first.get().getClass();
            }
        } else {
            final Optional<Po> first = oldCollection.stream().findFirst();
            if (first.isPresent()) {
                type = (Class<Po>) first.get().getClass();
            }
        }
        if (type == null) {
            throw new IllegalArgumentException("无法确定类型");
        }
        Javers javers = BasicEntityPoDiff.getJavers(type);

        final Changes changes = javers.compareCollections(oldCollection, newCollection, type).getChanges();
        if (changes.isEmpty()) {
            return;
        }
        Collection<Po> preAddCollection = new ArrayList<>();
        Collection<Po> preRemoveCollection = new ArrayList<>();
        Map<Po, Po> preUpdateCollection = new HashMap<>();
        for (Change change : changes) {
            if (change instanceof CollectionChange<?> listChange) {
                final List<ContainerElementChange> elementChanges = listChange.getChanges();
                for (ContainerElementChange elementChange : elementChanges) {
                    if (elementChange instanceof ValueAdded valueAdded) {
                        if (valueAdded.getAddedValue() instanceof InstanceId id) {
                            newCollection.stream().filter(i -> i.getId().equals(id.getCdoId())).findFirst().ifPresent(preAddCollection::add);
                        } else {
                            // 不会执行
                            preAddCollection.add((Po) valueAdded.getAddedValue());
                        }
                    }
                    if (elementChange instanceof ValueRemoved valueRemoved) {
                        if (valueRemoved.getRemovedValue() instanceof InstanceId id) {
                            oldCollection.stream().filter(i -> i.getId().equals(id.getCdoId())).findFirst().ifPresent(preRemoveCollection::add);
                        } else {
                            // 不会执行
                            preRemoveCollection.add((Po) valueRemoved.getRemovedValue());
                        }
                    }
                }
            }
            if (change instanceof ValueChange valueChange && valueChange.getClass() == ValueChange.class) {

                final Object id = valueChange.getAffectedLocalId();
                preUpdateCollection.put(newCollection.stream().filter(i -> i.getId().equals(id)).findFirst().orElseThrow(),
                        oldCollection.stream().filter(i -> i.getId().equals(id)).findFirst().orElseThrow());
            }

        }
        if (!preAddCollection.isEmpty()) {
            basicEntityPoDao.batchInsertPos(preAddCollection);
        }
        if (!preRemoveCollection.isEmpty()) {
            basicEntityPoDao.batchDeletePosById(preRemoveCollection);
        }
        if (!preUpdateCollection.isEmpty()) {
            basicEntityPoDao.batchDynamicUpdateById(preUpdateCollection);
        }
    }

    public static class BatchEntityWrapper<T> {

        private final String id;

        private final Collection<T> collection;


        public BatchEntityWrapper(String id, Collection<T> collection) {
            this.id = id;
            this.collection = collection;
        }
    }
}
